<template>
  <div class="container">
    <!-- 页面头部 -->
    <div class="header">
      <h1>🚄 火车票查询系统</h1>
      <p>快速查询，便捷出行</p>
    </div>

    <!-- 搜索区域 -->
    <div class="search-section">
      <el-button
        type="primary"
        @click="searchTickets"
        class="search-btn-plus"
        :loading="loading"
      >
        {{ loading ? "查询中..." : "🔍 批量查询余票" }}
      </el-button>
      <el-button
        type="success"
        @click="exportToExcel"
        class="export-btn-plus"
        :disabled="!tickets.length"
      >
        📊 导出为 Excel
      </el-button>
      <!-- 使用 Element Plus 表单组件 -->
      <el-form class="search-form-plus">
        <!-- 1. 日期选择器置顶 -->
        <el-form-item label="出发日期" class="date-picker-item">
          <el-date-picker
            v-model="searchDate"
            type="date"
            placeholder="请选择日期"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
            :min-date="minDate"
            :max-date="maxDate"
            style="width: 100%"
          />
        </el-form-item>

        <div
          class="form-row"
          v-for="(criterion, index) in allCriteria"
          :key="index"
        >
          <!-- 2. 出发地 - 多选框 -->
          <el-form-item label="出发地">
            <el-select
              v-model="criterion.departures"
              multiple
              filterable
              placeholder="可多选或输入搜索"
              style="width: 100%"
            >
              <el-option
                v-for="station in criterion.allStations"
                :key="station"
                :label="station"
                :value="station"
              />
            </el-select>
          </el-form-item>

          <!-- 3. 到达地 - 多选框 -->
          <el-form-item label="到达地">
            <el-select
              v-model="criterion.arrivals"
              multiple
              filterable
              placeholder="可多选或输入搜索"
              style="width: 100%"
            >
              <el-option
                v-for="station in criterion.allStations"
                :key="station"
                :label="station"
                :value="station"
              />
            </el-select>
          </el-form-item>

          <!-- 4. 车次号 - 可创建标签的多选框 -->
          <el-form-item label="车次号">
            <el-select
              v-model="criterion.trains"
              multiple
              filterable
              allow-create
              default-first-option
              placeholder="可多选或输入新车次, 支持英文逗号分隔输入后按回车"
              style="width: 100%"
              @create="(val) => handleTrainCreate(val, criterion)"
            >
              <!-- 渲染已有车次选项，便于选取 -->
              <el-option
                v-for="train in criterion.trains || []"
                :key="train"
                :label="train"
                :value="train"
              />
            </el-select>
          </el-form-item>
        </div>
      </el-form>
    </div>

    <!-- 查询结果 -->
    <div class="results-section">
      <div class="results-header">
        <span v-if="tickets.length">
          查询结果 ({{ tickets.length }} 趟车次)
        </span>
        <span v-else> 火车票查询结果 </span>
      </div>

      <div v-if="loading" class="loading">正在查询车票信息...</div>

      <transition-group name="fade" tag="div" v-else-if="tickets.length">
        <div
          v-for="ticket in tickets"
          :key="`${ticket.车次}-${ticket.出发地}`"
          class="ticket-card"
        >
          <div class="ticket-main">
            <div class="train-info">
              <div class="train-number">{{ ticket.车次 }}</div>
            </div>
            <div class="route-info">
              <div class="station">
                <div class="station-name">{{ ticket.出发地 }}</div>
                <div class="station-time">{{ ticket.出发时间 }}</div>
              </div>
              <div class="route-line"></div>
              <div class="station">
                <div class="station-name">{{ ticket.到达地 }}</div>
                <div class="station-time">{{ ticket.到达时间 }}</div>
              </div>
            </div>
            <div class="duration">
              <div>历时</div>
              <div style="font-weight: 600; color: #333; margin-top: 5px">
                {{ calculateDuration(ticket.出发时间, ticket.到达时间) }}
              </div>
            </div>
            <div class="seats-info">
              <div class="seats-title">所有余票情况</div>
              <div class="seat-types">
                <div
                  v-for="(count, type) in ticket.余票席位"
                  :key="type"
                  class="seat-item"
                >
                  {{ type }}: {{ count }}
                </div>
              </div>
            </div>
          </div>
          <div class="ticket-footer">
            <div class="date-info">出发日期: {{ formatDate(ticket.日期) }}</div>
          </div>
        </div>
      </transition-group>

      <div v-else class="no-results">
        <div style="font-size: 4rem; margin-bottom: 20px">🔍</div>
        <h3>暂无相关车次</h3>
        <p style="margin-top: 10px">请填写信息并点击按钮开始查询</p>
      </div>
    </div>
  </div>
</template>

<script setup>
import { onMounted, ref, onUnmounted } from "vue";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { formatAvailableTickets, formatCurrentDate } from "./utils";
import stations from "./assets/stations.json"; // 导入车站数据
import * as XLSX from "xlsx";

const allCriteria = ref(stations);

// --- 日期处理 ---
const formatDateForInput = (date) => {
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const day = date.getDate().toString().padStart(2, "0");
  return `${year}-${month}-${day}`;
};

const today = new Date();
const maxDay = new Date();
maxDay.setDate(today.getDate() + 14);
// const searchDate = ref(formatDateForInput(today));
// const searchDate = ref("2025-09-24");
const searchDate = ref("2025-10-01");
const minDate = ref(formatDateForInput(today));
const maxDate = ref(formatDateForInput(maxDay));

const loading = ref(false);
const tickets = ref([]);

/**
 * 封装的函数，用于处理单个查询标准 (优化后)
 * @param {Client} client - SDK 客户端实例
 * @param {object} criteria - 单个查询对象，包含 queryName, trains 等
 * @param {string} date - 查询日期
 * @returns {Promise<object[]>} - 返回格式化后的票务结果数组
 */
async function processCriteria(criteria, date) {
  const { departures, arrivals, trains, queryName } = criteria;
  const allResults = [];

  const combinationPromises = [];
  for (const fromCity of departures) {
    for (const toCity of arrivals) {
      const promise = client
        .callTool({
          name: "search",
          arguments: {
            date: date,
            fromCity: fromCity,
            toCity: toCity,
          },
        })
        .then((result) => {
          // 使用可选链和空值合并运算符安全地获取文本内容
          const textToParse = result?.content?.[0]?.text ?? null;
          if (!textToParse) {
            console.warn(
              `查询成功但内容为空: ${date} 从 ${fromCity} 到 ${toCity}`
            );
            return; // 内容为空，提前退出
          }

          // 一个简单的判断是看它是否以 '{' 或 '[' 开头。
          const trimmedText = textToParse.trim();
          if (!trimmedText.startsWith("{") && !trimmedText.startsWith("[")) {
            // 如果不是，说明返回的是非 JSON 的文本（很可能是错误信息），直接记录并退出。
            console.error(
              `响应非JSON格式: ${date} 从 ${fromCity} 到 ${toCity}. 收到的内容:`,
              textToParse
            );
            return; // 提前退出，不尝试解析
          }
          // --------------------------

          try {
            console.log(`查询成功: ${date} 从 ${fromCity} 到 ${toCity}`);
            const resultHandle = JSON.parse(textToParse);
            if (Array.isArray(resultHandle)) {
              allResults.push(...resultHandle);
            }
          } catch (e) {
            // 这个 catch 现在只处理那些看起来像 JSON 但实际上格式错误的情况
            console.error(
              `JSON 解析失败: ${date} 从 ${fromCity} 到 ${toCity}.`,
              `收到的内容: ${String(textToParse)}`, // 强制转为字符串以保证日志记录安全
              `错误详情: ${e.message}`
            );
          }
        })
        .catch((error) => {
          // 处理 Promise 本身的 rejection，例如网络中断
          console.error(
            `查询请求失败: ${date} 从 ${fromCity} 到 ${toCity}:`,
            error.message || "未知请求错误"
          );
        });
      combinationPromises.push(promise);
    }
  }

  await Promise.all(combinationPromises);

  return formatAvailableTickets(allResults, queryName, date, trains);
}
let transport, client;
const searchTickets = async () => {
  loading.value = true;
  tickets.value = [];
  const allTasks = allCriteria.value;
  transport = new SSEClientTransport(new URL("/sse", window.location.origin));
  client = new Client({
    name: "example-client",
    version: "1.0.0",
  });
  try {
    await client.connect(transport);
    console.log("SSE Transport started for batch search.");

    const promises = allTasks.map((criteria) =>
      processCriteria(criteria, searchDate.value)
    );

    const results = await Promise.allSettled(promises);

    const allFoundTickets = [];
    results.forEach((result, index) => {
      const queryName = allTasks[index].queryName;
      if (result.status === "fulfilled" && result.value.length > 0) {
        console.log(
          `✅ 任务 [${queryName}] 成功，找到 ${result.value.length} 个结果。`
        );
        allFoundTickets.push(...result.value);
      } else if (result.status === "rejected") {
        console.error(`❌ 任务 [${queryName}] 失败:`, result.reason);
      }
    });
    tickets.value = allFoundTickets;

    if (allFoundTickets.length === 0) {
      console.warn("未找到任何符合条件的车次，请检查查询条件或稍后再试。");
    } else {
      exportToExcel();
    }
  } catch (err) {
    console.error("执行批量查询时发生意外错误:", err);
  } finally {
    loading.value = false;
    await teardownMCP();
  }
};
async function teardownMCP() {
  try {
    // 关闭 MCP 客户端（异步）
    if (client?.close) await client.close();
  } catch (e) {
    console.warn("关闭 client 失败：", e);
  }

  try {
    // 关闭传输层（SSE/EventSource）
    if (transport?.close) await transport.close();
  } catch (e) {
    console.warn("关闭 transport 失败：", e);
  }

  client = null;
  transport = null;
}
const calculateDuration = (startTime, endTime) => {
  const start = new Date(`2000-01-01 ${startTime}`);
  const end = new Date(`2000-01-01 ${endTime}`);
  if (end < start) end.setDate(end.getDate() + 1);
  const diff = end - start;
  const hours = Math.floor(diff / 3600000);
  const minutes = Math.floor((diff % 3600000) / 60000);
  return `${hours}小时${minutes}分钟`;
};

const formatDate = (dateStr) => {
  const date = new Date(dateStr);
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");
  const day = date.getDate().toString().padStart(2, "0");
  const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
  const weekday = weekdays[date.getDay()];
  return `${year}年${month}月${day}日 ${weekday}`;
};

/**
 * 将查询结果导出为带有自定义标题的 Excel 文件
 */
const exportToExcel = () => {
  if (tickets.value.length === 0) {
    return; // 没有数据则不执行任何操作
  }

  // 1. 创建自定义标题行内容
  const titleText = `火车票日期: ${
    searchDate.value
  }  查询时间: ${formatCurrentDate("full")}`;
  const titleRow = [titleText];

  // 2. 定义数据表头
  const columnHeaders = [
    "查询组",
    "日期",
    "车次",
    "出发地",
    "到达地",
    "出发时间",
    "到达时间",
    "余票详情",
  ];

  // 3. 准备要导出的数据行
  const dataRows = tickets.value.map((ticket) => {
    const seatsString = Object.entries(ticket.余票席位)
      .map(([type, count]) => `${type}: ${count}`)
      .join("\n"); // 使用换行符分隔，在 Excel 中更美观

    return [
      ticket.查询组,
      ticket.日期,
      ticket.车次,
      ticket.出发地,
      ticket.到达地,
      ticket.出发时间,
      ticket.到达时间,
      seatsString,
    ];
  });

  // 4. 将标题、表头和数据组合成一个二维数组
  const sheetData = [titleRow, columnHeaders, ...dataRows];

  // 5. 使用 aoa_to_sheet 从二维数组创建工作表
  const ws = XLSX.utils.aoa_to_sheet(sheetData);

  // 6. 设置合并单元格
  // '!merges' 是一个特殊属性，用于定义合并区域
  // s: start, e: end, r: row, c: column (均为 0-based)
  ws["!merges"] = [
    { s: { r: 0, c: 0 }, e: { r: 0, c: columnHeaders.length - 1 } },
  ];

  // 7. 设置标题行的单元格样式（居中）
  // 注意：xlsx 的社区版对样式的支持有限，但此设置在多数 Excel 软件中有效
  const titleCell = ws["A1"]; // 获取标题单元格
  if (titleCell) {
    titleCell.s = {
      alignment: {
        horizontal: "center",
        vertical: "center",
      },
      font: {
        bold: true,
        sz: 14, // 字体大小
      },
    };
  }

  // 8. 设置列宽 (可选，但建议)
  ws["!cols"] = [
    { wch: 15 }, // 查询组
    { wch: 12 }, // 日期
    { wch: 10 }, // 车次
    { wch: 15 }, // 出发地
    { wch: 15 }, // 到达地
    { wch: 10 }, // 出发时间
    { wch: 10 }, // 到达时间
    { wch: 30 }, // 余票详情
  ];

  // 9. 创建工作簿并触发下载
  const wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, "余票查询结果");
  const fileName = `火车票查询结果_${formatCurrentDate("full")}.xlsx`;
  XLSX.writeFile(wb, fileName);
};

// 自动定时查询（使用 onMounted + onUnmounted 管理定时器）
// 注: 原先在模块顶层使用 setTimeout 可能在组件未挂载或被热重载时表现异常。
// 这里使用 setInterval 在组件挂载期间定期触发查询，并在卸载时清理。
const AUTO_SEARCH_INTERVAL_MS = 10 * 60 * 1000; // 10 分钟，按需调整为 10 * 60 * 1000
let autoSearchInterval = null;

onMounted(() => {
  console.log(`计划每 ${AUTO_SEARCH_INTERVAL_MS / 60000} 分钟自动查询一次。`);
  // 使用 setInterval 确保周期性触发。如果你只想在等待若干分钟后触发一次，请改用 setTimeout 并只运行一次。
  autoSearchInterval = setInterval(() => {
    console.log("自动查询触发: 调用 searchTickets()");
    // 只在组件仍然存在时调用
    try {
      searchTickets();
    } catch (e) {
      console.error("自动查询时发生错误:", e);
    }
  }, AUTO_SEARCH_INTERVAL_MS);
});

onUnmounted(() => {
  if (autoSearchInterval) {
    clearInterval(autoSearchInterval);
    autoSearchInterval = null;
    console.log("已清理自动查询定时器 (组件卸载)");
  }
});
</script>

<style scoped>
/* 为 Element Plus 表单添加特定样式 */
.search-form-plus {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.date-picker-item {
  margin-bottom: 0; /* 日期选择器不需要额外的下边距 */
}
.form-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 20px;
}
/* 为按钮组添加一些样式 */
.button-group {
  display: flex;
  gap: 15px; /* 按钮之间的间距 */
  margin-bottom: 20px;
}

.search-btn-plus,
.export-btn-plus {
  flex-grow: 1; /* 让按钮平分宽度 */
  height: 50px;
  font-size: 1.1rem;
  font-weight: 600;
  margin-bottom: 0; /* 覆盖之前的 margin */
}

/* 保持原有样式 */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  padding: 20px;
}
.container {
  max-width: 1200px;
  margin: 0 auto;
}
.header {
  text-align: center;
  margin-bottom: 40px;
  color: #333;
}
.header h1 {
  font-size: 3rem;
  margin-bottom: 10px;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.header p {
  font-size: 1.2rem;
  opacity: 0.9;
}
.search-section {
  background: white;
  border-radius: 20px;
  padding: 30px;
  margin-bottom: 30px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.results-section {
  background: white;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.results-header {
  background: linear-gradient(45deg, #667eea, #764ba2);
  color: white;
  padding: 20px 30px;
  font-size: 1.3rem;
  font-weight: 600;
}
.ticket-card {
  border-bottom: 1px solid #f0f0f0;
  padding: 25px 30px;
  transition: all 0.3s ease;
}
.ticket-card:hover {
  background: linear-gradient(45deg, #f8f9ff, #fff5f8);
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.ticket-card:last-child {
  border-bottom: none;
}
.ticket-main {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr 1.5fr;
  gap: 20px;
  align-items: center;
  margin-bottom: 20px;
}
.train-info {
  text-align: center;
}
.train-number {
  font-size: 1.8rem;
  font-weight: 700;
  color: #667eea;
  margin-bottom: 5px;
}
.route-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
}
.station {
  text-align: center;
  flex: 1;
}
.station-name {
  font-size: 1.3rem;
  font-weight: 600;
  color: #333;
  margin-bottom: 8px;
}
.station-time {
  font-size: 1.1rem;
  color: #667eea;
  font-weight: 500;
}
.route-line {
  flex: 2;
  height: 3px;
  background: linear-gradient(to right, #667eea, #764ba2);
  margin: 0 20px;
  border-radius: 2px;
  position: relative;
}
.route-line::before,
.route-line::after {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 10px;
  height: 10px;
  border-radius: 50%;
}
.route-line::before {
  left: -5px;
  background: #667eea;
}
.route-line::after {
  right: -5px;
  background: #764ba2;
}
.duration {
  text-align: center;
  color: #666;
  font-size: 0.9rem;
}
.seats-info {
  text-align: center;
}
.seats-title {
  font-size: 0.9rem;
  color: #666;
  margin-bottom: 10px;
}
.seat-types {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}
.seat-item {
  background: linear-gradient(45deg, #e8f2ff, #f0e8ff);
  border: 2px solid #667eea;
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 0.9rem;
  color: #333;
  font-weight: 500;
  transition: all 0.3s ease;
}
.seat-item:hover {
  background: linear-gradient(45deg, #667eea, #764ba2);
  color: white;
  transform: scale(1.05);
}
.ticket-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 15px;
  border-top: 1px solid #f0f0f0;
}
.date-info {
  color: #666;
  font-size: 0.9rem;
}
.no-results {
  text-align: center;
  padding: 60px 30px;
  color: #666;
}
.loading {
  text-align: center;
  padding: 40px;
  color: #667eea;
  font-size: 1.2rem;
  font-weight: 500;
}
.loading::after {
  content: "";
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 3px solid #667eea;
  border-radius: 50%;
  border-top-color: transparent;
  animation: spin 1s linear infinite;
  margin-left: 10px;
  vertical-align: middle;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
.fade-enter-active,
.fade-leave-active {
  transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
  transform: scale(0.95) translateY(20px);
}

@media (max-width: 768px) {
  .header h1 {
    font-size: 2rem;
  }
  .form-row {
    grid-template-columns: 1fr;
  }
  .ticket-main {
    grid-template-columns: 1fr;
    gap: 15px;
    text-align: center;
  }
  .route-info {
    flex-direction: column;
    gap: 15px;
  }
  .route-line {
    width: 3px;
    height: 40px;
    margin: 10px 0;
  }
}
</style>
